package com.ybear.ybmediax.media;

import android.app.Application;
import android.content.res.AssetFileDescriptor;
import android.media.MediaDataSource;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;

import com.ybear.ybmediax.cache.CacheHelper;
import com.ybear.ybmediax.cache.IMediaXCache;
import com.ybear.ybnetworkutil.http.Req;
import com.ybear.ybutils.utils.LogUtil;
import com.ybear.ybutils.utils.ObjUtils;

import java.io.File;
import java.io.FileDescriptor;
import java.net.HttpCookie;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

public class MediaXC implements IMediaXCache {
    private final String TAG = "MediaXC";
    private final CacheHelper mHelper = CacheHelper.get();
    private final Map<Integer, MediaXStatus> mMediaXStatusMap = new HashMap<>();
    private MediaX mMediaX;
    private int mCurrentTag = 0;

    private MediaXC() { }
    public static MediaXC get() { return HANDLER.I; }
    public static MediaXC newGet() { return new MediaXC(); }
    private static final class HANDLER { private static final MediaXC I = new MediaXC(); }

    /* CacheHelper */

    @Override
    public void init(@NonNull Application app) {
        mHelper.init( app );
        initXC( app );
    }

    @Override
    public void init(@NonNull Application app, @Nullable Req req) {
        mHelper.init( app, req );
        initXC( app );
    }

    @Override
    public void init(@NonNull Application app, @Nullable String rootPath) {
        mHelper.init( app, rootPath );
        initXC( app );
    }

    @Override
    public void init(@NonNull Application app, @Nullable Req req, @Nullable String rootPath) {
        mHelper.init( app, req, rootPath );
        initXC( app );

    }

    private void initXC(Application app) {
        mMediaX = new MediaX( app );
    }

    @Override
    public File get(String url) { return mHelper.get( url ); }

    private void addCache(Consumer<Boolean> call, boolean isAutoSetDataSource, String... urls) {
        if( urls == null || urls.length == 0 ) return;
        String logStr = "[%s] %s:%s | isAutoSetDataSource:" + isAutoSetDataSource;
        LinkedHashSet<String> localFiles = new LinkedHashSet<>();
        LinkedHashSet<String> onlineFiles = new LinkedHashSet<>();
        boolean isHaveLocalFiles;
        for( String url : urls ) {
            File file = get( url );
            //本地文件添加到本地列表
            if( file != null && file.exists() ){
                localFiles.add( file.getAbsolutePath() );
                continue;
            }
            //非本地文件添加到在线列表
            onlineFiles.add( url );
        }
        //本地文件列表
        if( isHaveLocalFiles = localFiles.size() > 0 ) {
            String[] localPaths = localFiles.toArray( new String[ 0 ] );
            //添加本地文件到播放列表
            if( isAutoSetDataSource ) mMediaX.setDataSource( true, localPaths );
            LogUtil.d( TAG, String.format( logStr,
                    "Local cache", "path", Arrays.toString( localPaths )
            ));
        }
        //请求下载缓存列表
        if( onlineFiles.size() > 0 ) {
            String[] onlineUrls = onlineFiles.toArray( new String[ 0 ] );
            //请求下载缓存
            mHelper.add( call, onlineFiles );
            //添加链接到播放列表
            if( isAutoSetDataSource ) mMediaX.setDataSource( !isHaveLocalFiles, onlineUrls );
            LogUtil.d( TAG, String.format( logStr,
                    "Online url", "url", Arrays.toString( onlineUrls )
            ));
        }
    }

    @Override
    public void add(Consumer<Boolean> call, LinkedHashSet<String> url) {
        addCache( call, false, url.toArray(new String[ 0 ] ) );
    }

    @Override
    public void add(Consumer<Boolean> call, String url) {
        addCache( call, true, url );
    }

    @Override
    public void setDataSource(String url) { add( null, url ); }

    @Override
    public void setDataSource(Consumer<Boolean> call, String url) { add( call, url ); }

    public void setDataSource(Uri... uris) {
        mMediaX.setDataSource( uris );
    }
    
    public void setDataSource(@NonNull Uri uri, @Nullable Map<String, String> headers) {
        mMediaX.setDataSource( uri, headers );
    }
    
    public void setDataSource(@NonNull Uri uri, @Nullable Map<String, String> headers, 
                              @Nullable List<HttpCookie> cookies) {
        mMediaX.setDataSource( uri, headers, cookies );
    }
    
    public void setDataSource(AssetFileDescriptor... assetFileDescriptors) {
        mMediaX.setDataSource( assetFileDescriptors );
    }

    public void setDataSource(FileDescriptor... fileDescriptors) {
        mMediaX.setDataSource( fileDescriptors );
    }

    public void setDataSource(@NonNull FileDescriptor fd, long offset, long length) {
        mMediaX.setDataSource( fd, offset, length );
    }
    
    public void setDataSource(MediaDataSource... mediaDataSources) {
        mMediaX.setDataSource( mediaDataSources );
    }

    @Override
    public void remove(String url) { mHelper.remove( url ); }

    @Override
    public void clear() { mHelper.clear(); }

    @Override
    public void setMaxQueue(int max) { mHelper.setMaxQueue( max ); }

    public void play(int tag, String url) { play( tag, url, 0 ); }

    public void play(int tag, String url, long delayed) {
        File file = mHelper.get( url );
        if( file != null && file.exists() ) {
            mMediaX.setDataSource( file.getAbsolutePath() );
            LogUtil.e( TAG, "play -> file:" + file.getAbsolutePath() );
        }else {
            mMediaX.setDataSource( url );
        }
        play( tag, delayed );
    }

    public void play(int tag) { play( tag, 0 ); }

    public void play(int tag, long delayed) {
        mCurrentTag = tag;
        mMediaX.play( delayed );
    }

//    public void on() {
//        mMediaX.on();
//    }
//
//    public void next() {
//        mMediaX.next();
//    }

//    public void select(int index) {
//        mMediaX.select( index );
//    }

    public void pause(int tag) {
        mCurrentTag = tag;
        mMediaX.pause();
    }

    public void stop(int tag) {
        mCurrentTag = tag;
        mMediaX.stop();
    }

    public void reset(int tag) {
        mCurrentTag = tag;
        mMediaX.reset();
    }

//    public void release() {
//        mMediaX.release();
//    }

    public MediaX seekTo(int tag, int progress) {
        mCurrentTag = tag;
        return mMediaX.seekTo( progress );
    }

    public MediaX setSpeed(float speed) { return mMediaX.setSpeed( speed ); }

    public MediaX setLooping(boolean enable) { return mMediaX.setLooping( enable ); }

    public boolean isLooping() { return mMediaX.isLooping(); }

    public boolean isPlaying() { return mMediaX.isPlaying(); }

    public Data getCurrentData() { return mMediaX.getCurrentData(); }

    public void addOnMediaStatusListener(int tag, MediaXStatus l) {
        mMediaXStatusMap.put( tag, l );
        mMediaX.setOnMediaStatusListener( new MediaXStatus(){
            @Override
            public void onPlay() {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onPlay();
            }

            @Override
            public void onPause() {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onPause();
            }

            @Override
            public void onStop() {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onStop();
            }

            @Override
            public void onReset() {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onReset();
            }

            @Override
            public void onRelease() {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onRelease();
            }

            @Override
            public void onCompletion(int currentPlayNum, int playTotal, boolean isCompletion) {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onCompletion( currentPlayNum, playTotal, isCompletion );
            }

            @Override
            public void onBufferingUpdate(int percent) {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) mxs.onBufferingUpdate( percent );
            }

            @Override
            public boolean onError(int what, int extra) {
                MediaXStatus mxs = getMediaXStatus();
                if( mxs != null ) return mxs.onError( what, extra );
                return false;
            }
        } );
    }

    /**
     创建一个Tag，用于验证回调的消息是否属于本身
     */
    public int createTag() {
        int tag = ObjUtils.parseInt( ( Math.random() * 10000000D ) + 10000000D );
        //检查重复性
        if( mMediaXStatusMap.containsKey( tag ) ) return createTag();
        return tag;
    }

    private MediaXStatus getMediaXStatus() {
        if( !mMediaXStatusMap.containsKey( mCurrentTag ) ) return null;
        return mMediaXStatusMap.get( mCurrentTag );
    }
}